using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Threading;
using ThreadPool;

namespace EventBroker
{
	/// <summary>
	/// Represents a m_strTopic subscription.
	/// </summary>
	internal class CSubscription
	{
		private WeakReference m_wrSubscriber;
		private string m_strHandlerMethodName;
        private EThreadOption m_eThreadOption;
		private RuntimeMethodHandle m_methodHandle;
		private Type m_handlerEventArgsType = null;
		private RuntimeTypeHandle m_typeHandle;
        private int m_nSelectedWorkItemGroupID;

        
		/// <summary>
		/// Initializes a new instance of the subscription class
		/// </summary>
        internal CSubscription(object objSubscriber_p,
                               string strHandlerMethodName_p,
                               Type[] aParameterTypes_p,
                               EThreadOption eThreadOption_p, 
                               int nSelectedWorkItemGroupID_p)
		{
			this.m_wrSubscriber = new WeakReference(objSubscriber_p);
			this.m_strHandlerMethodName = strHandlerMethodName_p;
            this.m_eThreadOption = eThreadOption_p;
            m_nSelectedWorkItemGroupID = nSelectedWorkItemGroupID_p;
					
			MethodInfo handler = GetMethodInfo(objSubscriber_p, strHandlerMethodName_p, aParameterTypes_p);
			this.m_typeHandle = objSubscriber_p.GetType().TypeHandle;
			this.m_methodHandle = handler.MethodHandle;
			ParameterInfo[] aParameters = handler.GetParameters();
		                
            ParameterInfo parameterInfo = handler.GetParameters()[1];
			Type type = parameterInfo.ParameterType;
			m_handlerEventArgsType = typeof(EventHandler<>).MakeGenericType(type);
		}


		private MethodInfo GetMethodInfo(object objSubscriber_p, string strHandlerMethodName_p, Type[] aParameterTypes_p)
		{
			if (aParameterTypes_p != null)
			{
				return objSubscriber_p.GetType().GetMethod(strHandlerMethodName_p, aParameterTypes_p);
			}
			else
			{
				return objSubscriber_p.GetType().GetMethod(strHandlerMethodName_p);
			}
		}


        private static bool IsValidEventHandler(ParameterInfo[] aParameters_p)
        {
            return aParameters_p.Length == 2 &&
                            typeof(EventArgs).IsAssignableFrom(aParameters_p[1].ParameterType);
        }


		/// <summary>
		/// The objSubscriber_p of the event.
		/// </summary>
		public object Subscriber
		{
			get { return m_wrSubscriber.Target; }
		}


		/// <summary>
		/// The dgEventTopicFire method m_strName that's subscribed to the event.
		/// </summary>
		public string HandlerMethodName
		{
			get { return m_strHandlerMethodName; }
		}


		internal dgEventTopicFireDelegate GetHandler()
		{
            return EventTopicFireHandler;
		}


		private void EventTopicFireHandler(object objSender_p, EventArgs eventArgs_p)
		{
			object objSubscriber = m_wrSubscriber.Target;
			if (objSubscriber == null)
			{
				return;
			}

            switch (m_eThreadOption)
            {
                case EThreadOption.Publisher:
                    CallOnPublisher(objSender_p, eventArgs_p);
                    break;
                case EThreadOption.Background:
                    CallOnBackgroundWorker(objSender_p, eventArgs_p);
                    break;
                default:
                    break;
            }
		}


		private void CallOnPublisher(object objSender_p, EventArgs eventArgs_p)
		{
			Delegate dgHandler = CreateSubscriptionDelegate();
            if (dgHandler != null)
			{
                dgHandler.DynamicInvoke(objSender_p, eventArgs_p);
			}			
		}


        private void CallOnBackgroundWorker(object obSender_p, EventArgs eventArgs_p)
        {
            Delegate handler = CreateSubscriptionDelegate();
            if (handler != null)
            {
                //(1)
                //ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object objState_p)
                //{
                //    CallInBackgroundArguments sArgs = (CallInBackgroundArguments)objState_p;
                //    sArgs.m_dHandler.DynamicInvoke(sArgs.m_objSender, sArgs.m_eventArgs);                    

                //}), new CallInBackgroundArguments(objSender_p, eventArgs_p, dgEventTopicFire));

                //(2)
                //Thread UserWorkItem = new Thread(delegate(object objState_p)
                //{
                //    CallInBackgroundArguments sArgs = (CallInBackgroundArguments)objState_p;
                //    sArgs.m_dHandler.DynamicInvoke(sArgs.m_objSender, sArgs.m_eventArgs);

                //});

                //UserWorkItem.IsBackground = true;
                //UserWorkItem.Start(new CallInBackgroundArguments(objSender_p, eventArgs_p, dgEventTopicFire));

                IWorkItemsGroup iWorkItemsGroup = SmartThreadPool.GetInstance().CreateWorkItemsGroup(m_nSelectedWorkItemGroupID);
                iWorkItemsGroup.QueueWorkItem(new WorkItemCallback(delegate(object objState_p)
                {
                    SCallInBackgroundArguments sArgs = (SCallInBackgroundArguments)objState_p;
                    sArgs.m_dgHandler.DynamicInvoke(sArgs.m_objSender, sArgs.m_eventArgs);    
                    return 1;

                }), new SCallInBackgroundArguments(obSender_p, eventArgs_p, handler));

            }
        }


		private Delegate CreateSubscriptionDelegate()
		{
			object objSubscriber = m_wrSubscriber.Target;
			if (objSubscriber != null)
			{
				return Delegate.CreateDelegate(this.m_handlerEventArgsType, 
                                               objSubscriber,
					                           (MethodInfo)MethodInfo.GetMethodFromHandle(m_methodHandle, m_typeHandle));
			}
			return null;
		}


        private struct SCallInBackgroundArguments
        {
            public Delegate m_dgHandler;
            public object m_objSender;
            public EventArgs m_eventArgs;
            
            public SCallInBackgroundArguments(object objSender_p, EventArgs eventArgs_p, Delegate dgHandler_p)
            {
                m_objSender = objSender_p;
                m_eventArgs = eventArgs_p;
                m_dgHandler = dgHandler_p;
            }
        }
	}
}
